package com.wjy.game.service;


import com.alibaba.fastjson.JSON;
import com.wjy.game.handler.Handler;
import com.wjy.game.message.ReqMessageData;
import com.wjy.game.message.Request;
import com.wjy.game.message.ResMessageData;
import com.wjy.game.message.Response;
import com.wjy.game.setting.ServerSetting;
import com.wjy.game.utils.ProtostuffUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * @author wjy
 * @date 2025/5/21 09:34
 * @description 连接服务
 */
@Slf4j
@Component
public class ClientSocketService {

    @Resource
    private ServerSetting setting;

    private Socket socket;

    private transient DataInputStream is;

    private transient DataOutputStream os;

    private Map<String, Handler> handlerMap;

    private transient ResponseThread thread;

    /**
     * 连接
     * @return
     */
    public synchronized boolean connect() {
        if (socket == null) {
            setting.load();
            try {
                socket = new Socket(setting.getIp(), setting.getPort());
                Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                    try {
                        socket.close();
                        log.info("关闭连接成功");
                    } catch (IOException e) {
                        log.error("关闭连接时发生异常，异常信息：{}", e.getMessage());
                    }
                }));
            } catch (IOException e) {
                log.error("和服务器建立连接发生异常，异常信息：{}", e.getMessage(), e);
                return false;
            }
        }
        thread = new ResponseThread();
        thread.start();
        return true;
    }

    /**
     * 重新连接
     * @return
     */
    public synchronized boolean reconnect() {
        log.info("开始重连服务器");
        if (checkConnect()) {
            boolean isClosed = close();
            return isClosed && connect();
        }
        if (socket != null) {
            socket = null;
        }
        if (thread != null) {
            thread.interrupt();
            thread = null;
        }
        return connect();
    }

    /**
     * 关闭连接
     * @return
     */
    public synchronized boolean close() {
        if (socket != null && !socket.isClosed()) {
            try {
                socket.shutdownInput();
                socket.shutdownOutput();
                if (is!= null) {
                    is.close();
                }
                if (os!= null) {
                    os.close();
                }
                socket.close();
                thread.interrupt();
                thread = null;
                log.info("关闭服务器连接成功");
            } catch (IOException e) {
                log.error("关闭服务器连接时发生异常，异常信息：{}", e.getMessage(), e);
                return false;
            }
        }
        return true;
    }

    public <T extends ReqMessageData> void request(Request<T> request) {
        if (checkConnect()) {
            byte[] data = ProtostuffUtil.serialize(request);
            try {
                if (os == null) {
                    os = new DataOutputStream(socket.getOutputStream());
                }
                if (data != null) {
                    MDC.put("traceId", request.getTraceId());
                    log.info("向服务器发送数据 | {}", JSON.toJSONString(request));
                    os.writeInt(data.length);
                    os.write(data);
                    os.flush();
                }
            } catch (IOException e) {
                log.error("向服务器发送数据发生异常，异常信息：{}", e.getMessage());
                close();
            } finally {
                MDC.remove("traceId");
            }
        }
    }

    public <T extends ResMessageData> Response<T> response() {
        if (checkConnect()) {
            try {
                if (is == null) {
                    is = new DataInputStream(socket.getInputStream());
                }
                int len = is.readInt();
                byte[] data = new byte[len];
                is.readFully(data);
                Response obj = ProtostuffUtil.deserialize(data, Response.class);
                MDC.put("traceId", obj.getTraceId());
                log.info("接收到服务器数据 | {}", JSON.toJSONString(obj));
                return obj;
            } catch (IOException e) {
                log.error("从服务器接收数据发生异常，异常信息：{}", e.getMessage());
                close();
                return null;
            }
        }
        return null;
    }

    private boolean checkConnect() {
        boolean isConnect = socket != null && !socket.isClosed() && socket.isConnected();
        if (!isConnect) {
            log.warn("未建立连接或连接已断开");
            if (thread != null) {
                thread.interrupt();
            }
        }
        return isConnect;
    }

    public void registerHandler(String traceId, Handler handler) {
        if (handlerMap == null) {
            handlerMap = new HashMap<>();
        }
        handlerMap.put(traceId, handler);
    }

    public void removeHandler(String traceId) {
        if (handlerMap != null) {
            handlerMap.remove(traceId);
        }
    }

    private class ResponseThread extends Thread {

        @Override
        public void run() {
            while (!this.isInterrupted()) {
                Response<ResMessageData> response = response();
                if (response!= null && handlerMap != null) {
                    String traceId = response.getTraceId();
                    Handler handler = handlerMap.get(traceId);
                    if (handler!= null) {
                        handler.handle(response);
                        removeHandler(traceId);
                        MDC.remove("traceId");
                    }
                }
            }
        }
    }
}
